OPC Studio User's Guide and Reference
Subscribing to a specific dataset field (OPC UA PubSub)
Client and Subscriber Development > Development Models > Imperative Programming Model > Imperative Programming Model for OPC UA PubSub > Subscribing to Information (OPC UA PubSub) > Subscribing to a specific dataset field (OPC UA PubSub)
In This Topic

The "native" granular unit of OPC UA PubSub data delivery is a dataset message. The dataset message can contain values of multiple fields. All these field values belong together - they represent a snapshot of some state of the system at a common time point. Individual fields can then be extracted from the dataset message as needed.

There are, however, situations in which your code is only interested in a specific field (or specific fields) of the dataset. For such cases, QuickOPC offers you a mechanism that allows you to subscribe to individual dataset fields, and the field extraction will be made for you by the component.

This functionality is not available under (or the text does not apply to) COM development platform.
For quick tests and experiments with OPC UA PubSub, and especially if you do not want to lose time coding when you need to figure out the proper settings, you can use the OpcCmd Utility - see Using OpcCmd Utility as OPC UA PubSub Subscriber. The utility allows to specify all parameters of the dataset subscriptions that are accessible from code, but simply by entering them on the command line. It also has a built-in support for displaying the incoming datasets and their fields.

How-To

Subscribing to specific dataset fields is very similar to subscribing to the whole datasets. A subscription to a single dataset field is initiated by a call to SubscribeDataSetField Method. This is an extension method, with multiple overloads. The primary form takes an argument that is an instance of EasyUASubscribeDataSetFieldArguments Class. This class is very similar to the EasyUASubscribeDataSetArguments Class, but it has an additional DataSetFieldDescriptor Property, which specifies the name of the dataset field to be subscribed to, or its dataset field Id (a GUID; this only works if metadata is available). There are implicit conversions from a string (representing the field name) or a GUID (representing the dataset field Id) to the UADataSetFieldDescriptor Class contained in this property, so you usually do not have to construct the dataset field descriptor in your code explicitly.

The are various overloads of the SubscribeDataSetField Method, and also various overloads of the EasyUASubscribeDataSetFieldArguments Constructor that allow you to make your subscription code shorter, depending on which pieces of information needed for the subscription you have at hand.

The SubscribeDataSetField Method(s) return an integer (dataset) field subscription handle. Call the UnsubscribeDataSetField Method, passing it this field subscription handle, in order to unsubscribe from the dataset field.

Note that as usual, both the SubscribeDataSetField Method a UnsubscribeDataSetField Method operate asynchronously. That is, you may start receiving callbacks before the SubscribeDataSetField Method returns, and there may be some final callbacks made even after the UnsubscribeDataSetField Method returns.

The callback that you pass in is of type EasyUADataSetFieldMessageEventHandler Delegate, and carries with it event arguments of type EasyUADataSetFieldMessageEventArgs Class. This class is very similar to the EasyUADataSetMessageEventArgs Class, except that instead of the data for the whole dataset message, it contains only data for the particular dataset field that you have subscribed to, in its FieldData Property. Successful establishment of a PubSub connection is indicated by a callback event arguments in which both the Exception Property and FieldData Property contain a null reference.

Pros and Cons

If you just need a specific dataset field, the code becomes simpler with the use of single-field subscriptions. Also, there is no big performance penalty: Even if you subscribe to multiple dataset fields using this mechanism, internally, QuickOPC makes just one PubSub connection to the message oriented middleware, and also all the message parsing, filtering and most of the processing is made just once. It is only the final message delivery steps that are split, and different.

The disadvantages related to the use of subscriptions to specific dataset fields are:

Examples

// This example shows how to subscribe to a single dataset field, resolving logical parameters to physical from an OPC-UA
// PubSub configuration file in binary format. The metadata obtained through the resolution is used to decode fixed layout
// messages with RawData field encoding.
//
// In order to produce network messages for this example, run the UADemoPublisher tool. For documentation, see
// https://kb.opclabs.com/UADemoPublisher_Basics . In some cases, you may have to specify the interface name to be used.
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .

using System;
using System.Threading;
using OpcLabs.EasyOpc.UA.PubSub;

namespace UADocExamples.PubSub._EasyUASubscriber
{
    class SubscribeDataSetField
    {
        public static void Main1()
        {
            // Define the PubSub resolver. We want the information be resolved from a PubSub binary configuration file that
            // we have. The file itself at the root of the project, and we have specified that it has to be copied to the
            // project's output directory.
            var pubSubResolverDescriptor = UAPubSubResolverDescriptor.File("UADemoPublisher-Default.uabinary");

            // Define the PubSub connection we will work with, using its logical name in the PubSub configuration.
            var pubSubConnectionDescriptor = new UAPubSubConnectionDescriptor { Name = "FixedLayoutConnection" };
            // In some cases you may have to set the interface (network adapter) name that needs to be used, similarly to
            // the statement below. Your actual interface name may differ, of course.
            //pubSubConnectionDescriptor.ResourceAddress.InterfaceName = "Ethernet";

            // Define the filter. The writer group and the dataset writer are specified using their logical names in the
            // PubSub configuration. The publisher Id in the filter will be taken from the logical PubSub connection.
            var filter = new UASubscribeDataSetFilter("FixedLayoutGroup", "SimpleWriter");

            // Instantiate the subscriber object.
            var subscriber = new EasyUASubscriber();

            Console.WriteLine("Subscribing...");
            int fieldHandle = subscriber.SubscribeDataSetField(
                pubSubResolverDescriptor, 
                pubSubConnectionDescriptor,
                filter,
                "Int32Fast",
                (sender, args) => { Console.WriteLine(args); });

            Console.WriteLine("Processing dataset message events for 20 seconds...");
            Thread.Sleep(20 * 1000);

            Console.WriteLine("Unsubscribing...");
            subscriber.UnsubscribeDataSetField(fieldHandle);

            Console.WriteLine("Waiting for 1 second...");
            // Unsubscribe operation is asynchronous, messages may still come for a short while.
            Thread.Sleep(1 * 1000);

            Console.WriteLine("Finished.");
        }

        // Example output:
        //
        //Subscribing...
        //Processing dataset message events for 20 seconds...
        //Success
        //Success; 1626 {System.Int32}; Good
        //Success; 1711 {System.Int32}; Good
        //Success; 1741 {System.Int32}; Good
        //Success; 1837 {System.Int32}; Good
        //Success; 1897 {System.Int32}; Good
        //Success; 1993 {System.Int32}; Good
        //Success; 2082 {System.Int32}; Good
        //Success; 2135 {System.Int32}; Good
        //Success; 2185 {System.Int32}; Good
        //Success; 2241 {System.Int32}; Good
        //Success; 2324 {System.Int32}; Good
        //Success; 2368 {System.Int32}; Good
        //Success; 2423 {System.Int32}; Good
        //Success; 2445 {System.Int32}; Good
        //Success; 2497 {System.Int32}; Good
        //Success; 2584 {System.Int32}; Good
        //Success; 2608 {System.Int32}; Good
        //...
    }
}
# This example shows how to subscribe to dataset messages on an OPC-UA PubSub connection, and then unsubscribe from that
# dataset.
#
# In order to produce network messages for this example, run the UADemoPublisher tool. For documentation, see
# https://kb.opclabs.com/UADemoPublisher_Basics . In some cases, you may have to specify the interface name to be used.
#
# Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
# OPC client and subscriber examples in Python on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-Python .
# The QuickOPC package is needed. Install it using "pip install opclabs_quickopc".
import opclabs_quickopc
import time

# Import .NET namespaces.
from OpcLabs.BaseLib import *
from OpcLabs.EasyOpc.UA.PubSub import *
from OpcLabs.EasyOpc.UA.PubSub.OperationModel import *


# Define the PubSub resolver. We want the information be resolved from a PubSub binary configuration file that
# we have. The file itself is in this script's directory.
pubSubResolverDescriptor = UAPubSubResolverDescriptor.File(ResourceDescriptor('UADemoPublisher-Default.uabinary'))

# Define the PubSub connection we will work with, using its logical name in the PubSub configuration.
pubSubConnectionDescriptor = UAPubSubConnectionDescriptor()
pubSubConnectionDescriptor.Name = 'FixedLayoutConnection'
# In some cases you may have to set the interface (network adapter) name that needs to be used, similarly to
# the statement below. Your actual interface name may differ, of course.
#pubSubConnectionDescriptor.ResourceAddress.InterfaceName = 'Ethernet'

# Define the filter. The writer group and the dataset writer are specified using their logical names in the
# PubSub configuration. The publisher Id in the filter will be taken from the logical PubSub connection.
filter = UASubscribeDataSetFilter(
    UAWriterGroupDescriptor('FixedLayoutGroup'),
    UADataSetWriterDescriptor('SimpleWriter'))

# Instantiate the subscriber object.
subscriber = EasyUASubscriber()

print('Subscribing...')
fieldHandle = IEasyUASubscriberExtension.SubscribeDataSetField(subscriber,
    pubSubResolverDescriptor,
    pubSubConnectionDescriptor,
    filter,
    UADataSetFieldDescriptor('Int32Fast'),
    EasyUADataSetFieldMessageEventHandler(lambda sender, args: print(args)))

print('Processing dataset message events for 20 seconds...')
time.sleep(20)

print('Unsubscribing...')
IEasyUASubscriberExtension.UnsubscribeDataSetField(subscriber, fieldHandle)

print('Waiting for 1 second...')
# Unsubscribe operation is asynchronous, messages may still come for a short while.
time.sleep(1)

print('Finished.')
' This example shows how to subscribe to a single dataset field, resolving logical parameters to physical from an OPC-UA
' PubSub configuration file in binary format. The metadata obtained through the resolution is used to decode fixed layout
' messages with RawData field encoding.
'
' In order to produce network messages for this example, run the UADemoPublisher tool. For documentation, see
' https://kb.opclabs.com/UADemoPublisher_Basics . In some cases, you may have to specify the interface name to be used.
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .

Imports OpcLabs.EasyOpc.UA.PubSub

Namespace PubSub._EasyUASubscriber
    Friend Class SubscribeDataSetField
        Public Shared Sub Main1()

            ' Define the PubSub resolver. We want the information be resolved from a PubSub binary configuration file that
            ' we have. The file itself is at the root of the project, and we have specified that it has to be copied to the
            ' project's output directory.
            Dim pubSubResolverDescriptor = UAPubSubResolverDescriptor.File("UADemoPublisher-Default.uabinary")

            ' Define the PubSub connection we will work with, using its logical name in the PubSub configuration.
            Dim pubSubConnectionDescriptor = New UAPubSubConnectionDescriptor
            pubSubConnectionDescriptor.Name = "FixedLayoutConnection"
            ' In some cases you may have to set the interface (network adapter) name that needs to be used, similarly to
            ' the statement below. Your actual interface name may differ, of course.
            ' pubSubConnectionDescriptor.ResourceAddress.InterfaceName = "Ethernet"

            ' Define the filter. The writer group and the dataset writer are specified using their logical names in the
            ' PubSub configuration. The publisher Id in the filter will be taken from the logical PubSub connection.
            Dim filter = New UASubscribeDataSetFilter("FixedLayoutGroup", "SimpleWriter")

            ' Instantiate the subscriber object.
            Dim subscriber = New EasyUASubscriber()

            Console.WriteLine("Subscribing...")
            Dim fieldHandle = subscriber.SubscribeDataSetField(
                pubSubResolverDescriptor,
                pubSubConnectionDescriptor,
                filter,
                "Int32Fast",
                Sub(sender, eventArgs)
                    Console.WriteLine(eventArgs)
                End Sub)

            Console.WriteLine("Processing dataset message events for 20 seconds...")
            Threading.Thread.Sleep(20 * 1000)

            Console.WriteLine("Unsubscribing...")
            subscriber.UnsubscribeAllDataSets()

            Console.WriteLine("Waiting for 1 second...")
            ' Unsubscribe operation is asynchronous, messages may still come for a short while.
            Threading.Thread.Sleep(1 * 1000)

            Console.WriteLine("Finished...")
        End Sub
    End Class

    ' Example output
    '
    'Subscribing...
    'Processing dataset message events for 20 seconds...
    'Success
    'Success; 1626 {System.Int32}; Good
    'Success; 1711 {System.Int32}; Good
    'Success; 1741 {System.Int32}; Good
    'Success; 1837 {System.Int32}; Good
    'Success; 1897 {System.Int32}; Good
    'Success; 1993 {System.Int32}; Good
    'Success; 2082 {System.Int32}; Good
    'Success; 2135 {System.Int32}; Good
    'Success; 2185 {System.Int32}; Good
    'Success; 2241 {System.Int32}; Good
    'Success; 2324 {System.Int32}; Good
    'Success; 2368 {System.Int32}; Good
    'Success; 2423 {System.Int32}; Good
    'Success; 2445 {System.Int32}; Good
    'Success; 2497 {System.Int32}; Good
    'Success; 2584 {System.Int32}; Good
    'Success; 2608 {System.Int32}; Good
    '...

End Namespace

 

See Also

Examples - OPC UA PubSub

Reference